using System;
using ACS.Compiler.Tables;
using ACS.Compiler.Tables.Modes;
using ACS.Compiler.Tree.Impl;

namespace ACS.Compiler.Synthes
{
    public class ModuleFormatter    
    {
        private CodeBase _code;

        public CodeBase Code
        {
            get { return _code; }
        }

        public ModuleFormatter(CodeBase module)
        {
            _code = module;
        }

        public void Format()
        {
            FormatHeader();
        }

        private void FormatHeader()
        {
            Formatter.Current.Format(
               "using System;\n"
             + "using System.Collections.Generic;\n"
             + "using System.Text;\n"
             + "using ACS.Runtime;\n"
             + "using ACS.Runtime.Types;\n"
             + "\n"
             + "namespace ACS.Generated"
                );
            Formatter.Current.Incr();
            Formatter.Current.Format("{");
            Formatter.Current.Format(String.Format("public class {0} : Module", Code.Namespace ));
            Formatter.Current.Incr();
            Formatter.Current.Format("{");
            Formatter.Current.Format(
                String.Format(
                  "private static {0} _instance;\n\n" +
                  "public static {0} Instance\n" +
                  "{{\n" +
                  "   get\n" +
                  "   {{\n" +
                  "      return _instance?? (_instance = new {0}());\n" +
                  "   }}\n" +
                  "}}", 
                  Code.Namespace ));
            FormatData();
            FormatProcedures();
            FormatModuleCode();
            FormatRunMethod();
            Formatter.Current.Decr();
            Formatter.Current.Format("\n}");
            Formatter.Current.Decr();
            Formatter.Current.Format("\n}");

            
        }

        private void FormatRunMethod()
        {
            Formatter.Current.Format(
                String.Format("public void Run ()"));
            FormatCode(Code.Module.Code);
        }

        private void FormatData()
        {
            foreach (DataField field in Code.Module.Fields)
            {
                string decl = field.Mode.ToDeclareFormat();
                string occ = "";
                ArrayMode arrMode = field.Mode as ArrayMode;
                if (arrMode != null)
                {
                    occ = arrMode.Occurs.ToString();
                }
                Formatter.Current.Format(String.Format("public {0} {1} = new {2}({3});", decl, field.Name, decl, occ));
            }
        }

        private void FormatModuleCode()
        {
            Formatter.Current.Format(
                String.Format("public static void Main (String[] args)"));
            Formatter.Current.Incr();
            Formatter.Current.Format("{");
            Formatter.Current.FormatString("Instance.Run();");            
            Formatter.Current.Decr();
            Formatter.Current.Format("\n}");
        }

        private void FormatProcedures()
        {
            foreach (Proc proc in Code.Module.Procedures)
            {
                FormatProcedure(proc);
            }
        }

        private void FormatProcedure(Proc proc)
        {
            string retString = ModesTable.Instance.TypeToString(proc.Procedure.RetMode);
            Formatter.Current.Incr();
            Formatter.Current.FormatString(
                String.Format("public {0} {1} (", retString, proc.Name ));
            bool first = true;
            foreach (DataField field in proc.Procedure.Parameters)
            {
                if (!first)
                {
                    Formatter.Current.Format(",");
                } else
                {
                    Formatter.Current.Format("");
                }
                first = false;
                string dataField = field.ToDeclareFormat();
                Formatter.Current.FormatString(dataField);
            }
            Formatter.Current.Decr();
            Formatter.Current.Format(")");
            FormatCode1(proc.Code, proc);
            Formatter.Current.Format("");
        }

        private void FormatCode1(Block code, Proc proc)
        {
            Formatter.Current.Incr();
            Formatter.Current.Format("{");
            foreach (DataField field in proc.DataFields)
            {
                string decl = field.Mode.ToDeclareFormat();
                string occ = "";
                ArrayMode arrMode = field.Mode as ArrayMode;
                if (arrMode != null)
                {
                    occ = arrMode.Occurs.ToString();
                }
                Formatter.Current.Format(String.Format("{0} {1} = new {2}({3});", decl, field.Name, decl, occ));
            }
            foreach (Statement statement in code.Stmts)
            {
                statement.Format();
            }
            Formatter.Current.Decr();
            Formatter.Current.Format("\n}");
        }

        private void FormatCode(Block code)
        {
            code.Format();
        }
    }
}
